import { Monitor } from ".."
import { getDeviceInfo } from "../utils/getDeviceInfo"
import pako from "pako"
import { DeviceInfoType } from "../error/error-type"
import { Base64 } from "js-base64"
import type { eventWithTime } from "@rrweb/types"
import { ErrorInfoType, RecordDataType } from "./transport-type"
import { PerformanceDataType } from "../performance/performance-type"
import { BLANKSCREENURL, ERRORINFOURL, MonitorConfig, PERFORMANCEURL, RECORDSCREENURL } from "../config"

export class Transport {
    monitor: InstanceType<typeof Monitor>
    deviceInfo: DeviceInfoType
    constructor(monitor: InstanceType<typeof Monitor>) {
        this.monitor = monitor
        this.deviceInfo = getDeviceInfo()

        // 页面加载时重新上传 上传失败的数据
        if (typeof window !== "undefined") {
            // window.addEventListener("load", this.retryFailedPerformanceData)
        }
    }

    async sendErrorInfo(errorData: ErrorInfoType) {
        const payload = this.getCommonPayload(errorData)
        const reportData = this.maybeCompressData(payload)
        await this.trySendReport(reportData, ERRORINFOURL)
    }

    async sendRecordScreen(data: RecordDataType) {
        // 1. 准备原始数据（此时events已经是压缩过的）
        const payload = this.getCommonPayload(data)

        try {
            const respone = await fetch("http://localhost:8118/record-screen", {
                method: "POST",
                body: JSON.stringify(payload),
                headers: {
                    "Content-Type": "application/json"
                }
            })
            return respone.ok
        } catch (error) {
            console.error("出错了", error)
        }
    }

    /**
     * sendBeacon优点：
     * 数据发送是可靠的
     * 数据异步传输
     * 不影响下一导航的载入
     */

    // 性能数据
    async sendPerformance(data: PerformanceDataType[]) {
        // 1、基础校验
        if (!data || !Array.isArray(data) || data.length === 0) {
            console.warn("No Performance data to send")
            return
        }

        const url = this.getReportUrl(PERFORMANCEURL)

        // 2、准备上报数据（简单深拷贝避免污染原始数据）
        const payload = JSON.parse(JSON.stringify(data))

        // 3、优先使用sendBeacon
        if (typeof navigator !== "undefined" && navigator.sendBeacon) {
            try {
                const blob = new Blob([JSON.stringify(payload)], { type: "application/json" })
                const success = navigator.sendBeacon(url, blob)

                if (success) {
                    // 上传成功
                    // console.log(`Sent ${data.length} records via Beacon`)
                    return // 🔴 关键修复：成功时直接返回
                } else {
                    // 上传失败，回退到 fetch
                    console.warn("Beacon failed, falling back to fetch 🛰")
                }
            } catch (beaconError) {
                console.warn("Beacon error:", beaconError)
            }
        }

        // 4、Beacon不可以用或失败时降级使用fetch
        try {
            // 如果时页面卸载阶段，不再尝试fetch(可能来不及)
            if (document.visibilityState === "hidden") {
                console.warn("Page unloading, skip fetch fallback")
                return
            }
            const respone = await fetch(url, {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify(payload),
                keepalive: true // 允许请求在页面关闭后继续
            })
            if (!respone.ok) {
                throw new Error(`HTTP error! status:${respone.status}`)
            }
            console.log(`Successfully sent ${data.length} records via fetch`)
        } catch (err) {
            console.error("Fetch fallback failed:", err)

            // 5、最终失败处理（存储到localStorage等）
            // try {
            //     // const failedData = JSON.parse(localStorage.getItem("failedPerfData") || "[]")
            //     // localStorage.setItem("failedPerData", JSON.stringify([...failedData, ...data]))
            //     console.warn("Stored failed data for later retry")
            // } catch (storageError) {
            //     console.error("Failed to store data:", storageError)
            // }
        }
    }

    // 添加重试逻辑（在页面加载时检查是否有失败的数据）
    // retryFailedPerformanceData() {
    //     if (typeof window === "undefined") return

    //     try {
    //         const failedData = JSON.parse(localStorage.getItem("failedPerData") || "[]")
    //         if (failedData.length > 0) {
    //             console.log(`Retrying ${failedData.length} failed records`)
    //             this.sendPerformance(failedData)
    //             localStorage.removeItem("failedPerfData")
    //         }
    //     } catch (error) {
    //         console.error("Failed to retry:", error)
    //     }
    // }

    // 发送白屏数据
    async sendBlankScreen(data: any) {
        const reportData = this.maybeCompressData(data)
        await this.trySendReport(reportData, BLANKSCREENURL)
    }

    // -------------------- 私有方法 --------------------
    private async trySendReport(data: Blob, type: string): Promise<boolean> {
        const url = this.getReportUrl(type)

        // 1. 优先尝试 Beacon API
        if (navigator.sendBeacon && data.size <= 64 * 1024) {
            try {
                if (navigator.sendBeacon(url, data)) return true
            } catch (e) {
                console.warn("Beacon 上报失败:", e)
            }
        }

        // 2. 降级方案：fetch + keepalive
        try {
            const response = await fetch(url, {
                method: "POST",
                body: data,
                headers: {
                    "Content-Type": data.type,
                    ...(data.type === "application/gzip" && { "Content-Encoding": "gzip" })
                },
                keepalive: true
            })
            return response.ok
        } catch (e) {
            console.warn("Fetch 上报失败:", e)
            return false
        }
    }

    private compressEvents(events: eventWithTime[]): string {
        return Base64.fromUint8Array(pako.gzip(JSON.stringify(events)))
    }

    private getCommonPayload(data: Record<string, unknown>) {
        return {
            ...data,
            appId: this.monitor.options.appId,
            userId: this.monitor.options.userId,
            sdkVersion: this.monitor.options.sdkVersion,
            deviceInfo: this.deviceInfo
        }
    }

    private maybeCompressData(data: Record<string, unknown>): Blob {
        const jsonStr = JSON.stringify(data)
        return jsonStr.length > 30 * 1024 ? new Blob([pako.gzip(jsonStr)], { type: "application/gzip" }) : new Blob([jsonStr], { type: "application/json" })
    }

    private getReportUrl(type: string): string {
        let url
        switch (type) {
            case PERFORMANCEURL:
                url = MonitorConfig.performanceUrl
                break
            case ERRORINFOURL:
                url = MonitorConfig.errorUrl
                break
            case BLANKSCREENURL:
                url = MonitorConfig.blankScreenUrl
                break
            case RECORDSCREENURL:
                url = MonitorConfig.recordScreenUrl
                break
            default:
                url = ""
                break
        }
        if (!url) throw new Error(`${type}上报URL未配置`)
        return url
    }
}
